#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "esp_check.h"
#include "nvs_flash.h"

#include <dirent.h>
#include <fnmatch.h>

#include "driver/sdmmc_host.h"
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"

#include "sdcard_list.h"
#include "sdcard_scan.h"

#include "audio_common.h"
#include "audio_element.h"
#include "audio_event_iface.h"
#include "audio_hal.h"
#include "audio_pipeline.h"
#include "esp_codec_dev.h"
#include "esp_decoder.h"
#include "fatfs_stream.h"
#include "filter_resample.h"
#include "i2s_stream.h"
#include "esp_decoder.h"

#include "tinyusb.h"
#include "tusb_msc_storage.h"
#include "board.h"

#define TAG "main"
#define BASE_PATH "/sdcard"

static struct  // 文件索引
{
    int id;
    char* url;
    char dirpath[128];
    char dirname[64];
} file_index = {
    .id = 0,
    .url = NULL,
    .dirpath = BASE_PATH,
};

audio_pipeline_handle_t pipeline_handle = NULL;
playlist_operator_handle_t sdcard_list_handle = NULL;
audio_element_handle_t i2s_stream_writer,audio_decoder,fatfs_stream_reader;//rsp_handle;

int dir_name_match(const char* dirpath, const char* pattern, char* dirname) {
    DIR* dir = opendir(dirpath);
    if (dir == NULL) {
        printf("Dir open failed\n");
        return 1;
    }
    struct dirent* entry = NULL;
    while ((entry = readdir(dir)) != NULL) {
        if (fnmatch(pattern, entry->d_name, FNM_CASEFOLD) == 0) {
            strcpy(dirname, entry->d_name);
            closedir(dir);
            return 0;
        }
    }
    printf("Dir not matched\n");
    return 2;
}

static void msc_event_cb(tinyusb_msc_event_t* event) {
    ESP_LOGI(TAG, "Storage mounted to application: %s", event->mount_changed_data.is_mounted ? "Yes" : "No");
}

static void sdmmc_vfs_init(void) {
    uint8_t retry = 3;

    sdmmc_card_t* sd_card = NULL;
    sdmmc_host_t host = SDMMC_HOST_DEFAULT();
    //host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;

    sdmmc_slot_config_t slot_config = ELY_SD_SLOT_CONFIG_DEFAULT();
    // sd_card = (sdmmc_card_t*)malloc(sizeof(sdmmc_card_t));
    //  ESP_ERROR_CHECK(host.init());
    //  ESP_ERROR_CHECK(sdmmc_host_init_slot(host.slot, (const sdmmc_slot_config_t*)&slot_config));

    esp_vfs_fat_mount_config_t mount_config = {
        .format_if_mount_failed = false,
        .max_files = 5,
        .allocation_unit_size = 16 * 1024};

    while (esp_vfs_fat_sdmmc_mount(BASE_PATH, &host, (const void*)&slot_config, &mount_config, &sd_card)) {
        vTaskDelay(1000 / portTICK_RATE_MS);
        ESP_LOGE(TAG, "SD card not detected, Retrying...");
        if (--retry == 0) {
            ESP_LOGE(TAG, "No SD card detected, aborting");
            host.deinit();
            free(sd_card);
            return;
        }
    }
    sdmmc_card_print_info(stdout, sd_card);
    sdmmc_card_print_info(stdout, sd_card);

    const tinyusb_msc_sdmmc_config_t msc_cfg = {
        .card = sd_card,
        .callback_mount_changed = msc_event_cb,
        .mount_config.max_files = 5,
    };
    ESP_ERROR_CHECK(tinyusb_msc_storage_init_sdmmc(&msc_cfg));
    ESP_ERROR_CHECK(tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, msc_event_cb));

    tinyusb_config_t tinyusb_cfg = {
        .self_powered = true,
        .vbus_monitor_io = GPIO_NUM_1,
        .external_phy = false,
    };
    ESP_ERROR_CHECK(tinyusb_driver_install(&tinyusb_cfg));
}

void sdcard_url_save_cb(void* user_data, char* url) {
    playlist_operator_handle_t sdcard_handle = (playlist_operator_handle_t)user_data;
    esp_err_t ret = sdcard_list_save(sdcard_handle, url);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Fail to save sdcard url to sdcard playlist");
    }
}
static void audio_playlist() {
    sdcard_list_create(&sdcard_list_handle);
    sdcard_scan(sdcard_url_save_cb, "/sdcard", 1, (const char*[]){ "mp3","flac"}, 2, sdcard_list_handle);
    sdcard_list_show(sdcard_list_handle);
}
void app_main(void) {
    sdmmc_vfs_init();

    audio_playlist();
    dir_name_match(file_index.dirpath, "00*", file_index.dirname);
    ESP_LOGI(TAG, "dir name: %s", file_index.dirname);

    audio_board_handle_t board_handle = audio_board_init();
    audio_hal_ctrl_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_DECODE, AUDIO_HAL_CTRL_START);

    audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
    pipeline_handle = audio_pipeline_init(&pipeline_cfg);
    mem_assert(pipeline_handle);

    i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();
    i2s_cfg.type = AUDIO_STREAM_WRITER;
    i2s_stream_writer = i2s_stream_init(&i2s_cfg);

    //mp3_decoder_cfg_t mp3_cfg = DEFAULT_MP3_DECODER_CONFIG();
    //mp3_decoder = mp3_decoder_init(&mp3_cfg);
    //flac_decoder_cfg_t flac_cfg = DEFAULT_FLAC_DECODER_CONFIG();
    //flac_decoder = flac_decoder_init(&flac_cfg);
    esp_decoder_cfg_t dec_cfg = DEFAULT_ESP_DECODER_CONFIG();

    audio_decoder_t audio_dec[] = {

        DEFAULT_ESP_MP3_DECODER_CONFIG(),

        DEFAULT_ESP_FLAC_DECODER_CONFIG()

    };
    audio_decoder = esp_decoder_init(&dec_cfg, audio_dec, 2);

    //rsp_filter_cfg_t rsp_cfg = DEFAULT_RESAMPLE_FILTER_CONFIG();
    //rsp_handle = rsp_filter_init(&rsp_cfg);

    sdcard_list_current(sdcard_list_handle, &file_index.url);
    fatfs_stream_cfg_t fatfs_cfg = FATFS_STREAM_CFG_DEFAULT();
    fatfs_cfg.type = AUDIO_STREAM_READER;
    fatfs_stream_reader = fatfs_stream_init(&fatfs_cfg);
    audio_element_set_uri(fatfs_stream_reader, file_index.url);

    audio_pipeline_register(pipeline_handle, fatfs_stream_reader, "file");
    audio_pipeline_register(pipeline_handle, audio_decoder, "dec");
    audio_pipeline_register(pipeline_handle, i2s_stream_writer, "i2s");

    const char* link_tag[3] = {"file","dec", "i2s"};
    audio_pipeline_link(pipeline_handle, &link_tag[0], 3);
    audio_pipeline_run(pipeline_handle);

    audio_event_iface_cfg_t audio_evt_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG();
    audio_event_iface_handle_t audio_evt_handle = audio_event_iface_init(&audio_evt_cfg);

    audio_pipeline_set_listener(pipeline_handle, audio_evt_handle);

    audio_event_iface_msg_t audio_evt_msg;
    esp_err_t ret;

    // 主循环
    while (1) {
        ret = audio_event_iface_listen(audio_evt_handle, &audio_evt_msg, portMAX_DELAY);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "Event interface error : %d", ret);
            continue;
        }
        if (audio_evt_msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT) {
            // 当音乐信息报告完毕时，设置当前音乐的信息
            if (audio_evt_msg.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO) {
                audio_element_info_t music_info = {0};

                if(audio_evt_msg.source == (void *) audio_decoder)
                    audio_element_getinfo(audio_decoder, &music_info);
                ESP_LOGI(TAG, "Received music sample_rates=%d, bits=%d, ch=%d, time=%d",
                music_info.sample_rates, music_info.bits, music_info.channels, music_info.duration);
                audio_element_setinfo(i2s_stream_writer, &music_info);
                //rsp_filter_set_src_info(rsp_handle, music_info.sample_rates, music_info.channels);
                continue;
            }
            // 当上一个音乐播放完毕时，切换到下一首音乐
            if (audio_evt_msg.source == (void *) i2s_stream_writer
                && audio_evt_msg.cmd == AEL_MSG_CMD_REPORT_STATUS) {
                audio_element_state_t el_state = audio_element_get_state(i2s_stream_writer);
                if (el_state == AEL_STATE_FINISHED) {
                    ESP_LOGI(TAG, "Finished, advancing to the next song");
                    sdcard_list_next(sdcard_list_handle, 1, &file_index.url);
                    ESP_LOGW(TAG, "URL: %s", file_index.url);
                    audio_element_set_uri(fatfs_stream_reader, file_index.url);
                    audio_pipeline_reset_ringbuffer(pipeline_handle);
                    audio_pipeline_reset_elements(pipeline_handle);
                    audio_pipeline_change_state(pipeline_handle, AEL_STATE_INIT);
                    audio_pipeline_run(pipeline_handle);
                }
                continue;
            }
        }
    }
}
